// ====== Computer Graphics Experiment #6 ======
// |   Two-Dimensional Viewing and Clipping    |
// =============================================
//
// Requirement:
// (1) Implement Cohen-Sutherland line clipping algorithm.
// (2) Change position and size of window and viewport  
//     and observe the effects.

#include <windows.h>
#include <GL/glut.h>
#include <math.h>

// 2D point class
class CPoint2D
{
public:
	float x, y;
};

// Rectangle class
class CRect
{
public:
	float xmin, ymin, xmax, ymax;

	float width(void)  { return xmax-xmin; }
	float height(void) { return ymax-ymin; }

	// Make (xmin, ymin) the lower left corner
	void normalize(void);

	// Draw the rectangle
	void draw(GLenum mode);

};

void CRect::normalize(void)
{
	float ftemp;
	if (xmin > xmax) 
		{ftemp=xmin; xmin=xmax; xmax=ftemp;}
	if (ymin > ymax) 
		{ftemp=ymin; ymin=ymax; ymax=ftemp;}
}

void CRect::draw(GLenum mode)
{
	glBegin(mode);
	glVertex2f(xmin, ymin);
	glVertex2f(xmax, ymin);
	glVertex2f(xmax, ymax);
	glVertex2f(xmin, ymax);
	glEnd();
}

#define PI 3.14159265359

int running_state=0;
// 0 --- Normal state. 
// 1 --- Rubber-band state. 

// Size of the scene
float scene_size=1000.0;

CRect clip_rect;	// Clipping rectangle
CRect window;	// Window
CRect viewport;		// Viewport

// Program window size
int pw_width, pw_height;

// Set window
void set_window(CRect *cw)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D (cw->xmin, cw->xmax, cw->ymin, cw->ymax);
}

// Set viewport
void set_viewport(CRect *vp)
{
	glViewport(vp->xmin, vp->ymin, 
		vp->width(), vp->height()); 
}


// Cohen-Sutherland line clipping algorithm.
bool line_clipping(CPoint2D p1, CPoint2D p2, CRect *cw, 
				   CPoint2D *q1, CPoint2D *q2)
// p1, p2: End points of input line segment
// cw:	   Clipping rectangle
// q1, q2: End points of output line segment
// Return value: true --- accept, false --- reject
{
	// Write your code here
}

//Translate the clip rectangle
void Translaterect(int dx,int dy)
{
  // Write your code here
}

// Initialization function
void init(void)
{
	glClearColor (0.0, 0.0, 0.0, 0.0);
	glEnable(GL_LINE_STIPPLE);
}

// Display callback function
void display(void)
{
	int i;
	CPoint2D p1, p2, q1, q2;
	double a, r;

	glClear (GL_COLOR_BUFFER_BIT);

	// Draw blue rectangle to fill the background
	glColor3f(0.0, 0.0, 0.5);
	window.draw(GL_POLYGON);

	// Draw unclipped lines in green color
	p1.x=0.0; p1.y=0.0;
	r=0.5*scene_size;
	glLineStipple(1, 0x0f0f);
	glColor3f(0.0, 1.0, 0.0);
	glBegin(GL_LINES);
	for (i=0; i<360; i+=15)
	{
		a=(double)i/180.0*PI;
		p2.x=r*cos(a);
		p2.y=r*sin(a);
		if (i==0 || i==180) p2.y=0;
		if (i==90 || i==270) p2.x=0;
		p2.x+=p1.x;
		p2.y+=p1.y;
		glVertex2f(p1.x, p1.y);
		glVertex2f(p2.x, p2.y);
	}
	glEnd();
	glLineStipple(1, 0xffff);

	// Draw clipped lines in white color
	if (running_state == 0) {
	glColor3f(1.0, 1.0, 1.0);
	glLineWidth(2.0);
	glBegin(GL_LINES);
	for (i=0; i<360; i+=15)
	{
		a=(double)i/180.0*PI;
		p2.x=r*cos(a);
		p2.y=r*sin(a);
		if (i==0 || i==180) p2.y=0;
		if (i==90 || i==270) p2.x=0;
		p2.x+=p1.x;
		p2.y+=p1.y;
		if(line_clipping(p1, p2, &clip_rect, &q1, &q2))
		{
			glVertex2f(q1.x, q1.y);
			glVertex2f(q2.x, q2.y);
		}
	}
	glEnd();
	glLineWidth(1.0);
	}

	// Draw clipping rectangle
	glLineStipple(1, 0x0f0f);
	glColor3f(1.0, 1.0, 0.0);
	clip_rect.draw(GL_LINE_LOOP);
	glLineStipple(1, 0xffff);

	glutSwapBuffers();
}

// Reshape callback function
void reshape(int w, int h)
{
	// Store program window size
	pw_width=w;
	pw_height=h;

	// set viewport
	viewport.xmin=0;
	viewport.xmax=w;
	viewport.ymin=0;
	viewport.ymax=h;
	set_viewport(&viewport);

	// set clipping window
	window.xmin=-0.6*scene_size;
	window.xmax=0.6*scene_size;
	window.ymin=-0.6*scene_size;
	window.ymax=0.6*scene_size;
	set_window(&window);

	// set clipping rectangle
	clip_rect.xmin=0.5*window.xmin;
    clip_rect.xmax=0.5*window.xmax;
	clip_rect.ymin=0.5*window.ymin;
    clip_rect.ymax=0.5*window.ymax;
}

// Keyboard callback function
void keyboard(unsigned char key, int x, int y)
{
	switch (key) {
		case 27:
			exit(0);
	}
}

// Special keyboard callback function
void special_key(int key, int x, int y)
{
	switch (key) {
      case GLUT_KEY_LEFT:
		// Write your code here
      case GLUT_KEY_RIGHT:
       // Write your code here
      case GLUT_KEY_DOWN:
        // Write your code here
      case GLUT_KEY_UP:
        // Write your code here
	}
}

// Main program entrance
int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE);
	glutInitWindowSize(500, 500);
	glutCreateWindow("Test 2D Clipping and Viewing");
	init();
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutSpecialFunc(special_key);
	glutDisplayFunc(display);
	glutMainLoop();
	return 0;
}
